package com.speedchina.oauth2_permission.web.sys.api_v1;

import com.speedchina.common.redis.service.RedisService;
import com.speedchina.common.rsa.aop.RequestRSA;
import com.speedchina.common.rsa.resolver.RequestBodyRSA;
import com.speedchina.common.rsa.service.RSAService;
import com.speedchina.framework.helper.Assert;
import com.speedchina.framework.model.R;
import com.speedchina.oauth2_permission.aspect.SysLog;
import com.speedchina.oauth2_permission.utils.SecurityUtils;
import com.speedchina.permission.base.sysenum.UserMsgEnum;
import com.speedchina.permission.base.sysenum.UserStatus;
import com.speedchina.permission.domain.sys.dto.ResourceDTO;
import com.speedchina.permission.domain.sys.entity.User;
import com.speedchina.permission.domain.sys.po.UserPo;
import com.speedchina.permission.service.sys.ResourceService;
import com.speedchina.permission.service.sys.RoleService;
import com.speedchina.permission.service.sys.SysLogLoginService;
import com.speedchina.permission.service.sys.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *  菜单管理接口
 * @author winter
 * @date 2021-04-08 15:19:42
 */
@RequestMapping("auth")
@RestController
@Api(description = "系统-权限相关 接口")
@Slf4j
public class HomeController {
    @Autowired
    UserService userService;
    @Autowired
    UserDetailsService userDetailsService;
    @Autowired
    ResourceService resourceService;
    @Autowired
    RoleService roleService;
    @Autowired
    RSAService rsaService;
    @Autowired
    RedisService redisService;
    @Value("${speedchina.permission.userLockTime:1800}")
    Long lockTime;
    @Value("${speedchina.permission.userTryLoginNum:5}")
    Integer tryLoginNum;
    @Autowired
    SysLogLoginService logLoginService;
    @Autowired
    TokenEndpoint tokenEndpoint;
    @Autowired
    ConsumerTokenServices tokenServices;
    @Autowired
    private OAuth2ClientProperties oauth2ClientProperties;
    /**192
     * 获取用户的菜单按钮权限
     * @return
     */
    @ApiOperation("查询用户信息")
    @SysLog("查询用户信息byToken")
    @GetMapping("/userinfo")
    public R userinfo(){
        User user = SecurityUtils.getLoginUser();
        return R.data(userService.getByUserName(user.getUsername()));
    }
    @ApiOperation("查询用户菜单树")
    @SysLog("查询用户信息byToken")
    @GetMapping("/userMenu")
//    @RequiresPermissions("123")
    public R userMenu(){
        User user = SecurityUtils.getLoginUser();
        List<ResourceDTO> list = userService.queryUserMenuTree(user);
        return R.data(list);
    }
    @ApiOperation("查询用户角色、权限集合")
    @SysLog("查询用户角色、权限集合")
    @GetMapping("/permission")
    public R userPermission(){
        User user = SecurityUtils.getLoginUser();
        Set<String> roles = roleService.queryUserRoles(user);
        Map<String,Object> data = new HashMap<>(2);
        data.put("permission",resourceService.getPermissions(user.getId()));
        data.put("roles",roles);
        return R.data(data);
    }
    @GetMapping("/getPublicKey")
    public R getPublicKey() throws Exception {
        Map<String,String> map = rsaService.generatePublicKeyPrivateKey();
        log.debug(map.get("publicKey"));
        log.debug(map.get("privateKey"));
        return R.data(map.get("publicKey"));
    }
    @ApiOperation("管理员重置用户密码")
    @SysLog("管理员重置用户密码")
    @PutMapping("resetPassword")
    @RequestRSA
    public R resetPassword(String username,String password){
        User user = userService.getByUserName(username);
        Assert.isNull(user, UserMsgEnum.USER_NOT_EXIST.getMsg());
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String newPassword = passwordEncoder.encode(password);
        return R.data(userService.resetUserPasswordOauth2(user.getId(),newPassword));
    }
    @ApiOperation("重置用户密码")
    @SysLog("重置用户密码")
    @PutMapping("resetUserPassword")
    @RequestRSA
    public R resetUserPassword(String username,String password,String newPwd){
        User user = userService.getByUserName(username);
        Assert.isNull(user, UserMsgEnum.USERNAME_OR_PASSWORD_ERROR.getMsg());
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if(!passwordEncoder.matches(user.getPassword(),password)){
            return R.error(UserMsgEnum.USERNAME_OR_PASSWORD_ERROR.getMsg());
        }
        String newPassword = passwordEncoder.encode(newPwd);
        return R.data(userService.resetUserPasswordOauth2(user.getId(),newPassword));
    }
    @ApiOperation("test")
    @SysLog("test")
    @PutMapping("test")
    @RequestRSA
    public R test(UserPo userPo,String newPwd){
        System.err.println(userPo.getUsername());
        System.err.println(userPo.getPassword());
        return R.ok();
    }
    /**
     * 普通登录入口，支持用户名、密码以及其他验证场景
     * @param model
     * @return
     */
//    @SysLog("登录")
    @ApiOperation("登录")
    @PostMapping("login")
    public R login(@RequestBodyRSA UserPo model) {
        // 正则： 手机号（可走验证码）、邮箱、普通用户名, 目前先支持 普通用户名
        User user = userService.getByUserName(model.getUsername());
        if(user == null) {
            // 迷惑性提示
            return R.error(UserMsgEnum.USERNAME_OR_PASSWORD_ERROR.getMsg());
        }
        if (null == user.getStatus()) {
            return R.error(UserMsgEnum.USER_INFO_EXCEPTION.getMsg());
        } else if (UserStatus.DISABLED.equals(user.getStatus())) {
            return R.error(UserMsgEnum.USER_DISABLE.getMsg());
        } else if (UserStatus.LOCKED.equals(user.getStatus())) {
            return R.error(UserMsgEnum.USER_LOCKING.getMsg());
        }
        if(redisService.get("trylogin_num_"+user.getUsername())!=null){
            int tryNum = Integer.valueOf(redisService.get("trylogin_num_"+user.getUsername()).toString());
            log.debug("{}",tryNum);
            if(tryNum==tryLoginNum){
                redisService.set("trylogin_num_"+user.getUsername(),tryLoginNum+1,lockTime);
                return R.error(UserMsgEnum.USER_LOCKING_30MIN.getMsg());
            }else if(tryNum>tryLoginNum-1){
                return R.error(UserMsgEnum.USER_LOCKING_30MIN.getMsg());
            }
        }
        Map<String, String> params = createClientParameters(model.getUsername(),model.getPassword(),"password");
        try {
            OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(createClientToken(model.getUsername()),params).getBody();
            // 成功登录, 返回用户基本信息、权限标记、菜单信息
            Map<String,Object> data = new HashMap<>(1);
            data.put("user",user);
            data.put("jwtToken",oAuth2AccessToken.getValue());
            logLoginService.saveSysLogLogin(user,oAuth2AccessToken.getValue());
            return R.data(data);
        } catch (HttpRequestMethodNotSupportedException e) {
            e.printStackTrace();
            if(redisService.get("trylogin_num_"+user.getUsername())==null){
                redisService.set("trylogin_num_"+user.getUsername(),1,lockTime);
            }else {
                redisService.incr("trylogin_num_"+user.getUsername(),1);
            }
        }
        return R.error(UserMsgEnum.USERNAME_OR_PASSWORD_ERROR.getMsg());
    }

//    @SysLog("退出登录")
    @ApiOperation("退出登录")
    @PostMapping("logout")
    public R logout() {
        // 考虑做黑名单, 在黑名单（即已经退出的用户）中，则验证token时，做非法处理
//        User user = SecurityUtils.getLoginUser();
        String token;
        try {
            token= SecurityUtils.getToken();
            tokenServices.revokeToken(token);
            logLoginService.updateLogLogin(token);
        }catch (Exception e){

        }
        return R.data(SecurityUtils.logout());
    }

    @SysLog("系统-权限 解除用户锁定")
    @ApiOperation("系统-权限 解除用户锁定")
    @PostMapping("unLockUser")
    public R unLockUser(@RequestBodyRSA UserPo model) {
        Assert.isEmpty(model.getUsername(),UserMsgEnum.USERNAME_IS_NOT_BLANK.getMsg());
        if(!redisService.hasKey("trylogin_num_"+model.getUsername())){
            return R.data(true);
        }
        return R.data(redisService.del("trylogin_num_"+model.getUsername()));
    }
//    public R userLogin(@RequestBodyRSA UserPo model){
//        //封装所需要的参数
//        Map<String, String> parameters = createClientParameters(model.getUsername(),model.getPassword(), "password");
//
//        User u = new User(oauth2ClientProperties.getClientId(), oauth2ClientProperties.getClientSecret());
//        //生成验证过的clientUsertoken
//        AccountLoginToken token = new AccountLoginToken(u,new ArrayList<>());
//
//        try {
//            OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(token, parameters).getBody();
//            return WebResponse.success(oAuth2AccessToken);
//        } catch (Exception e) {
//            log.error("登录失败！！" + e.getMessage());
//        }
//        return R.error(UserMsgEnum.USERNAME_OR_PASSWORD_ERROR.getMsg());
//    }
    //构造客户端参数
    private Map<String, String> createClientParameters(String username, String password,String grantType) {
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put("username", username);
        parameters.put("password", password);
        parameters.put("grant_type", grantType);
        return parameters;
    }
    //构造客户端Token
    private UsernamePasswordAuthenticationToken createClientToken(String username) {
        //客户端信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        org.springframework.security.core.userdetails.User u = new org.springframework.security.core.userdetails.User(oauth2ClientProperties.getClientId(), oauth2ClientProperties.getClientSecret(),userDetails.getAuthorities());
        //生成已经认证的client
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(u, null, userDetails.getAuthorities());
        return token;
    }
}
